package com.jimmy.medical.wechat;

import lombok.extern.slf4j.Slf4j;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Component
public class WeChatPayRequest {

    private static final String TAG = WeChatPayRequest.class.getName();

    @Autowired
    private WeChatPayConfig weChatPayConfig;

    @Autowired
    WeChatHttp weChatHttp;

    @Autowired
    WeChatSign weChatSign;

    /**
     * 请求交易流水
     */
    public Map<String, String> requestOrderFlow(String tradeNo, String tradeType, BigDecimal totalAmount, String description, String detail, String ipStr) {
        if (StringUtils.isEmpty(tradeNo) || totalAmount == null || StringUtils.isEmpty(description) || StringUtils.isEmpty(ipStr)) {
            log.info(TAG, "--------requestOrderFlow 参数错误--------");
            return null;
        }
        //初始化参数MAP // 参数名ASCII码从小到大排序（字典序）；
        SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
        //公众账号ID
        parameterMap.put("appid", weChatPayConfig.APPID);
        //商品描述
        parameterMap.put("body", description);
        parameterMap.put("detail", detail);
        //标价币种(默认人民币：CNY)
        parameterMap.put("fee_type", "CNY");
        //商户号
        parameterMap.put("mch_id", weChatPayConfig.MCH_ID);
        //随机字符串(32位)
        parameterMap.put("nonce_str", getRandomString(32));
        //异步接收微信支付结果通知的回调地址
        parameterMap.put("notify_url", weChatPayConfig.REDIRECT_URI_HOME);
        //重新组建新的交易号
//        tradeNo = resetTradeNo(tradeNo);
        //商户系统内部订单号，要求32个字符内、且在同一个商户号下唯一
        parameterMap.put("out_trade_no", tradeNo);
        //网页支付提交用户端ip
        if(ipStr.equals("0:0:0:0:0:0:0:1")) {
            ipStr = "118.114.107.121";
        }
        parameterMap.put("spbill_create_ip", ipStr);
        //订单总金额，单位为分
        BigDecimal total = totalAmount.multiply(new BigDecimal(100));
        java.text.DecimalFormat df = new java.text.DecimalFormat("0");
        parameterMap.put("total_fee", df.format(total));
        //交易类型
        parameterMap.put("trade_type", "APP");

        //创建交易签名
        String sign = createSign("UTF-8", parameterMap);
        parameterMap.put("sign", sign);
        //将参数打包为标准的xml格式数据
        String requestXML = getRequestXml(parameterMap);
        //提交请求
        String result = weChatHttp.httpsRequest(weChatPayConfig.API_URL_MCHPAY_UNIFIEDORDER, "POST", requestXML);
        //读取返回XML格式数据，转换为MAP数据
        Map<String, String> map = null;
        if (result == null) {
            return map;
        }
        try {
            map = doXMLParse(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    public SortedMap<String, Object> getPaySignParams(String prepayId) {
        if (StringUtils.isEmpty(prepayId)) {
            return null;
        }
        SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();
        finalpackage.put("appid", weChatPayConfig.APPID);
        finalpackage.put("noncestr", getRandomString(32));
        finalpackage.put("package", "Sign=WXPay");
        finalpackage.put("partnerid", weChatPayConfig.MCH_ID);
        finalpackage.put("prepayid", prepayId);
        finalpackage.put("timestamp", weChatSign.createTimestamp());
        String sign = createSign("UTF-8", finalpackage);
        finalpackage.put("sign", sign);
        return finalpackage;
    }

    /**
     * 随机字符串生成
     */
    public String getRandomString(int length) { //length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 请求xml组装
     */
    public String getRequestXml(SortedMap<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "detail".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");
            } else {
                sb.append("<" + key + ">" + value + "</" + key + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 生成签名
     */
    public String createSign(String characterEncoding, SortedMap<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + weChatPayConfig.API_KEY);
        log.info(TAG, "[createSign] param:" + sb.toString());
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        log.info(TAG, "[createSign] sign:" + sign);
        return sign;
    }

    /**
     * 验证回调签名
     *
     * @param characterEncoding
     * @param map
     * @return
     */
    public boolean isTenpaySign(String characterEncoding, Map<String, String> map) {
        String charset = "utf-8";
        String signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
            log.info(TAG, "API返回的数据签名数据不存在，有可能被第三方篡改!!!");
            return false;
        }
        log.info(TAG, "服务器回包里面的签名是:" + signFromAPIResponse);
        // 过滤空 设置 TreeMap
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + weChatPayConfig.API_KEY);
        // 将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
        String resultSign = "";
        String tobesign = sb.toString();
        // 算出签名
        if (null == charset || "".equals(charset)) {
            resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();
        } else {
            try {
                resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();
            } catch (Exception e) {
                resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();
            }
        }
        String tenpaySign = ((String) packageParams.get("sign")).toUpperCase();
        return tenpaySign.equals(resultSign);
    }

    /**
     * 退款请求
     */
    public String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {
        @SuppressWarnings("unused")
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        StringBuilder res = new StringBuilder("");
//          FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));  
//          try {  
//              keyStore.load(instream, "".toCharArray());  
//          } finally {  
//              instream.close();  
//          }  
//
//          // Trust own CA and all self-signed certs  
//          SSLContext sslcontext = SSLContexts.custom()  
//                  .loadKeyMaterial(keyStore, "1313329201".toCharArray())  
//                  .build();  
//          // Allow TLSv1 protocol only  
//          SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(  
//                  sslcontext,  
//                  new String[] { "TLSv1" },  
//                  null,  
//                  SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
//          CloseableHttpClient httpclient = HttpClients.custom()  
//                  .setSSLSocketFactory(sslsf)  
//                  .build();  
//          try {  
//
//              HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");  
//              httpost.addHeader("Connection", "keep-alive");  
//              httpost.addHeader("Accept", "*/*");  
//              httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");  
//              httpost.addHeader("Host", "api.mch.weixin.qq.com");  
//              httpost.addHeader("X-Requested-With", "XMLHttpRequest");  
//              httpost.addHeader("Cache-Control", "max-age=0");  
//              httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");  
//               StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8);  
//               httpost.setEntity(entity2);  
//              WXLog.Log(TAG,"executing request" + httpost.getRequestLine());  
//
//              CloseableHttpResponse response = httpclient.execute(httpost);  
//               
//              try {  
//                  HttpEntity entity = response.getEntity();  
//                    
//                  WXLog.Log(TAG,"----------------------------------------");  
//                  WXLog.Log(TAG,response.getStatusLine());  
//                  if (entity != null) {  
//                      WXLog.Log(TAG,"Response content length: " + entity.getContentLength());  
//                      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));  
//                      String text = "";
//                      res.append(text);  
//                      while ((text = bufferedReader.readLine()) != null) {  
//                          res.append(text);  
//                          WXLog.Log(TAG,text);  
//                      }  
//                       
//                  }  
//                  EntityUtils.consume(entity);  
//              } finally {  
//                  response.close();  
//              }  
//          } finally {  
//              httpclient.close();  
//          }  
        return res.toString();
    }

    /**
     * xml解析
     */
    public Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();

        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    public String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    /**
     * 重新包装TRADNO
     *
     * @param tradeno
     * @return
     */
    public String resetTradeNo(String tradeno) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return formatter.format(new Date()) + "_" + tradeno;
    }

    /**
     * 重新包装TRADNO
     *
     * @param tradenoWithKey
     * @return
     */
    public String regetTradeNo(String tradenoWithKey) {
        if (StringUtils.isEmpty(tradenoWithKey)) {
            return null;
        }
        String[] key = tradenoWithKey.split("_");
        if (key.length < 2) {
            return key[0];
        } else {
            return key[1];
        }
    }
}
